/*
 * Copyright (C) 2019
 * <tanghaifeng-gz@loongson.cn> <pengren.mcu@qq.com>
 *
 * SPDX-License-Identifier:	GPL-2.0+
 *
 */
#include <common.h>
#include <dm.h>
#include <clk.h>
#include <clk-uclass.h>
#include <bloblist.h>
#include <handoff.h>
#include <asm/io.h>

#include <mach/ls2k500.h>

DECLARE_GLOBAL_DATA_PTR;

static void calc_clocks(void)
{
	u32 ls2x_refclk = OSC_CLK / 1000; //参考时钟固定为100MHz
	u32 ctrl = 0;
//	u32 gmac_clk;
	unsigned int l1div_out, l1div_loopc, l1div_ref;
	unsigned int l2div_out;
	unsigned int mult, div;

	/* node cpu clk */
	ctrl = readl(LS2X_NODE_PLL_L);
	l1div_out = (ctrl >> NODE_L1DIV_OUT_SHIFT) & NODE_L1DIV_OUT_MARK;
	l1div_loopc = (ctrl >> NODE_L1DIV_LOOPC_SHIFT) & NODE_L1DIV_LOOPC_MARK;
	l1div_ref = (ctrl >> NODE_L1DIV_REF_SHIFT) & NODE_L1DIV_REF_MARK;
	mult = l1div_loopc;
	div = l1div_ref * l1div_out;
	gd->cpu_clk = (unsigned long)((ls2x_refclk * mult / div) * 1000);

	/* ddr net hda clk */
	ctrl = readl(LS2X_DDR_PLL_L);
	l1div_out = (ctrl >> DDR_L1DIV_OUT_SHIFT) & DDR_L1DIV_OUT_MARK;
	l1div_loopc = (ctrl >> DDR_L1DIV_LOOPC_SHIFT) & DDR_L1DIV_LOOPC_MARK;
	l1div_ref = (ctrl >> DDR_L1DIV_REF_SHIFT) & DDR_L1DIV_REF_MARK;
	ctrl = readl(LS2X_DDR_PLL_H);
	mult = l1div_loopc;
	div = l1div_ref * l1div_out;
	gd->mem_clk = (unsigned long)((ls2x_refclk * mult / div) * 1000);

	/* sb gpu gmac clk */
	ctrl = readl(LS2X_SOC_PLL_L);
	l1div_out = (ctrl >> SOC_L1DIV_OUT_SHIFT) & SOC_L1DIV_OUT_MARK; //gpu
	l1div_loopc = (ctrl >> SOC_L1DIV_LOOPC_SHIFT) & SOC_L1DIV_LOOPC_MARK;
	l1div_ref = (ctrl >> SOC_L1DIV_REF_SHIFT) & SOC_L1DIV_REF_MARK;
	mult = l1div_loopc;
	div = l1div_ref * l1div_out;
//	gpu_clk = (unsigned long)((ls2x_refclk * mult / div) * 100);
	ctrl = readl(LS2X_SOC_PLL_H);
	l2div_out = (ctrl >> SOC_L2DIV_OUT_GMAC_SHIFT) & SOC_L2DIV_OUT_GMAC_MARK;
	div = l1div_ref * l2div_out;
//	gmac_clk = (unsigned long)((ls2x_refclk * mult / div) * 100);

	l2div_out = (ctrl >> SOC_L2DIV_OUT_SB_SHIFT) & SOC_L2DIV_OUT_SB_MARK;
	div = l1div_ref * l2div_out;
	gd->bus_clk = (unsigned long)((ls2x_refclk * mult / div) * 1000);

	gd->arch.pll_clk = (unsigned long)(ls2x_refclk * 1000);
}

/* arch specific CPU init after DM */
int arch_cpu_init_dm(void)
{
	int ret;
	struct udevice *dev;

	ret = uclass_get_device(UCLASS_CLK, 0, &dev);
	if (ret) {
		printf("clk-uclass not found\n");
		return 0;
	}

	return 0;
}

int mach_cpu_init(void)
{
	calc_clocks();

	return 0;
}

int dram_init(void)
{
#if 0
	gd->ram_size = get_ram_size ((long *)CONFIG_SYS_SDRAM_BASE, CONFIG_SYS_SDRAM_SIZE);
#else
	if (fdtdec_setup_mem_size_base() != 0)
		return -EINVAL;
#endif
	return 0;
}

int dram_init_banksize(void)
{
	fdtdec_setup_memory_banksize();

	return 0;
}

#if defined(CONFIG_HANDOFF)
int handoff_arch_save(struct spl_handoff *ho)
{
	if (gd->arch.memorysize_total == 0) {
		gd->arch.memorysize_total = CONFIG_SYS_SDRAM_SIZE;
	}
	ho->arch.memorysize_total = gd->arch.memorysize_total;
	return 0;
}
#endif

#if defined(CONFIG_SPL) && !defined(CONFIG_SPL_BUILD)
void board_add_ram_info(int use_default)
{
	struct spl_handoff *ho;

	ho = bloblist_find(BLOBLISTT_SPL_HANDOFF, sizeof(*ho));
	if (!ho) {
		puts("Missing SPL hand-off info");
		gd->arch.memorysize_total = SZ_256M;
	} else {
		gd->arch.memorysize_total = ho->arch.memorysize_total;
	}

	puts("(can be use by u-boot)");
	putc('\n');
	puts("DRAM Total: ");
	print_size(gd->arch.memorysize_total, "");
}
#else
void board_add_ram_info(int use_default)
{
	register int raw_memsz asm ("k1");

	gd->arch.memorysize_total = raw_memsz & 0xff;
	gd->arch.memorysize_total = gd->arch.memorysize_total << 29;
	if (gd->arch.memorysize_total == 0) {
		gd->arch.memorysize_total = CONFIG_SYS_SDRAM_SIZE;
	}
	puts("(can be use by u-boot)");
	putc('\n');
	puts("DRAM Total: ");
	print_size(gd->arch.memorysize_total, "");
}
#endif

const char *get_core_name(void)
{
	u32 proc_id;
	const char *str;

	proc_id = read_c0_prid() & 0xffff;
	switch (proc_id) {
	case 0x0000a000:
		str = "LS2K500";
		break;
	default:
		str = "Unknown";
	}

	return str;
}

int print_cpuinfo(void)
{
	printf("Core: %s\n", get_core_name());
	printf("Speed: Cpu @ %ld MHz/ Mem @ %ld MHz/ Bus @ %ld MHz\n",
			gd->cpu_clk/1000000, gd->mem_clk/1000000, gd->bus_clk/1000000);
	return 0;
}
